package com.glsc.ngateway.platform.utils.owncloud;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.github.pagehelper.util.StringUtil;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.BaseSystemConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.SystemConfig;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.oc.domain.OcFilecache;
import com.glsc.ngateway.common.oc.repo.OcFilecacheRepository;
import com.glsc.ngateway.common.base.utils.EncodeUtils;
import com.glsc.ngateway.platform.service.SystemConfigService;
import com.glsc.ngateway.platform.utils.AssertOverride;
import com.google.gson.JsonObject;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Component
public class OwncloudUtils {

    static Logger logger = LoggerFactory.getLogger(OwncloudUtils.class);

    private static SystemConfigService systemConfigService;

    private static OcFilecacheRepository filecacheRepo;

    private static final String OWNCLOUD_CONFIG_CODE_HOME_URL = "OWNCLOUD_HOME_URL";

    private static final String OWNCLOUD_CONFIG_CODE_ADMIN_NAME_AND_PASSWORD = "OWNCLOUD_ADMIN_USERNAME_AND_PASSWORD";

//    private static final String OWNCLOUD_CONFIG_CODE_SESSION_FILTER = "OWNCLOUD_SESSION_FILTER";

    private static OwncloudUtils owncloudUtils;

    @Resource
    public void setSystemConfigService(SystemConfigService systemConfigService) {
        OwncloudUtils.systemConfigService = systemConfigService;
    }

    @Resource
    public void setFilecacheRepo(OcFilecacheRepository filecacheRepo) {
        OwncloudUtils.filecacheRepo = filecacheRepo;
    }

    @Lazy
    @Resource
    public void setOwncloudUtils(OwncloudUtils owncloudUtils) {
        OwncloudUtils.owncloudUtils = owncloudUtils;
    }

    /**
     * @return
     */
    public static String getConfig(String code) {
        return getConfig(code, false);
    }

    public static String getConfig(String code, boolean nullable) {
        Optional<SystemConfig> optional = Optional.ofNullable(systemConfigService.findByCode(code));
        if (!nullable && optional.isEmpty()) {
            throw PlatformException.error("未找到配置信息:" + code);
        }
        return optional.map(BaseSystemConfig::getConfigValue).orElse(null);
    }

    /**
     * 新增文件夹
     */
    public static HttpResponse<String> addDir(String url) throws UnsupportedEncodingException {
        return addDir(url, false, null);
    }

    public static HttpResponse<String> addDir(String url, boolean async, Consumer<HttpResponse<String>> rollback) throws UnsupportedEncodingException {
        // /remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/啊啊啊
        String urlRequest = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + EncodeUtils.encodeChinese(url);
        HttpResponse<String> res = doneRequest(urlRequest, "MKCOL", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofString(), null, async, rollback);
        return res;
    }

    /**
     * 新增文件
     */
    public static HttpResponse<String> addFile(String url, MultipartFile file) throws IOException {
        return addFile(url, file, false, null);
    }

    public static HttpResponse<String> addFile(String url, MultipartFile file, boolean async, Consumer<HttpResponse<String>> rollback) throws IOException {
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/资管子.xlsx
        String urlRequest = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + EncodeUtils.encodeChinese(url + "/" + file.getOriginalFilename());
        return doneRequest(urlRequest, "PUT", HttpRequest.BodyPublishers.ofByteArray(file.getBytes()), HttpResponse.BodyHandlers.ofString(), null, async, rollback);
    }

    /**
     * 下载文件
     */
    public static HttpResponse<byte[]> downloadFile(String path) throws UnsupportedEncodingException {
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/深圳债券规则调整业务指引V1.2（UF20&融资融券）.docx
        String url = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + EncodeUtils.encodeChinese(path);
        return doneRequest(url, "GET", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofByteArray(), null);
    }

    /**
     * 下载文件夹
     */
    public static HttpResponse<byte[]> downloadDir(String url) {
        int lastIdx = url.lastIndexOf("/");
        String dir = "/" + (lastIdx == -1 ? "" : url.substring(0, lastIdx));
        String files = url.substring(lastIdx + 1);
        ///index.php/apps/files/ajax/download.php?dir=/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规&files=XXX
        String requestUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/index.php/apps/files/ajax/download.php?dir=" + URLEncoder.encode(dir, StandardCharsets.UTF_8) + "&files=" + files;
        return doneRequest(requestUrl, "GET", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofByteArray(), null);
    }

    /**
     * 删除文件/文件夹
     */
    public static HttpResponse<String> delDirOrFile(String url) {
        // 定义请求URL
        //删除文件
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/资管子.xlsx
        //删除文件夹
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/XXX
        String mkcolUrl = null;
        try {
            mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + EncodeUtils.encodeChinese(url);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return doneRequest(mkcolUrl, "DELETE", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofString(), null);
    }

    /**
     * 重命名
     */
    public static HttpResponse<String> renameDirOrFile(String oldName, String newName) throws UnsupportedEncodingException {
        // 定义请求URL
        //文件
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/资管子.xlsx
        //header:Destination:http://10.1.81.200:33110/remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/资管子2.xlsx
        //文件夹
        ///remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/XXX
        //header:Destination:/remote.php/webdav/信息技术总部/01_系统文档/01_系统/01_交易清算系统/UF20/01_文档/01_功能文档/3.用户操作手册/20220514深圳债券新规/XXX2
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + EncodeUtils.encodeChinese(oldName);
        HashMap<String, String> headers = new HashMap<>(1) {{
            put("Destination", getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/webdav/" + newName);
//            put("Destination", owncloudHomeUrl + "/remote.php/webdav/12345/aaab.pdf");
        }};
        return doneRequest(mkcolUrl, "MOVE", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofString(), headers);
    }

    /**
     * 共享-添加
     */
    public static HttpResponse<String> share() {
        // 定义请求URL
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json";
        HashMap<String, String> headers = new HashMap<>(2) {{
            put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            put("Cookie", AssertOverride.notBlank(getSessionCookie(true), "获取owncloud session失败"));
        }};
        String body =
                //0-分享给人，1-分享给组
//                "shareType=" + "1" +
//                        "&shareWith=" + URLEncoder.encode("信息技术总部", StandardCharsets.UTF_8) +
//                        "&path=" + URLEncoder.encode("/123456/aaa.pdf", StandardCharsets.UTF_8);

                "shareType=" + "0" +
                        "&shareWith=" + URLEncoder.encode("xuchang", StandardCharsets.UTF_8) +
                        "&path=" + URLEncoder.encode("/123456/aaa.pdf", StandardCharsets.UTF_8) +
//                        不同权限可以相加
//                            OCS_PERMISSION_READ = 1
//                            OCS_PERMISSION_UPDATE = 2
//                            OCS_PERMISSION_CREATE = 4
//                            OCS_PERMISSION_DELETE = 8
//                            OCS_PERMISSION_SHARE = 16
//                            OCS_PERMISSION_ALL = 31
                        "&permissions=3";
        return doneRequest(mkcolUrl, "POST", HttpRequest.BodyPublishers.ofString(body), HttpResponse.BodyHandlers.ofString(), headers);
    }

    /**
     * 共享-修改权限
     * TODO: 需要根据文件路径、分享人找到分享记录id
     */
    public static HttpResponse<String> shareUpdate() {
        // 定义请求URL
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/ocs/v2.php/apps/files_sharing/api/v1/shares/" + "34" + "?format=json";
        HashMap<String, String> headers = new HashMap<>(2) {{
            put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            put("Cookie", AssertOverride.notBlank(getSessionCookie(true), "获取owncloud session失败"));
        }};
        String body =   //不同权限可以相加
                //    OCS_PERMISSION_READ = 1
                //    OCS_PERMISSION_UPDATE = 2
                //    OCS_PERMISSION_CREATE = 4
                //    OCS_PERMISSION_DELETE = 8
                //    OCS_PERMISSION_SHARE = 16
                //    OCS_PERMISSION_ALL = 31
                "permissions=19";
        return doneRequest(mkcolUrl, "PUT", HttpRequest.BodyPublishers.ofString(body), HttpResponse.BodyHandlers.ofString(), headers);
    }

    /**
     * 共享-删除
     * TODO: 需要根据文件路径、分享人找到分享记录id
     */
    public static HttpResponse<String> shareDelete() {
        // 定义请求URL
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/ocs/v2.php/apps/files_sharing/api/v1/shares/" + "34" + "?format=json";
        HashMap<String, String> headers = new HashMap<>(2) {{
            put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            put("Cookie", AssertOverride.notBlank(getSessionCookie(true), "获取owncloud session失败"));
        }};
        return doneRequest(mkcolUrl, "DELETE", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofString(), headers);
    }

    /**
     * 添加评论
     */
    @Nullable
    public static HttpResponse<String> addComment(String msg, String url) {
        ///remote.php/dav/comments/files/201013/
        OcFilecache filecache = filecacheRepo.findByPath(url);
        if (Objects.isNull(filecache)) {
            logger.info("未找到需要评论的文件/文件夹");
            return null;
        }
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/dav/comments/files/" + filecache.getFileid() + "/";
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("actorType", "users");
        jsonObject.addProperty("verb", "comment");
        jsonObject.addProperty("message", msg);
        HashMap<String, String> headers = new HashMap<>(2) {{
            put("Content-Type", "application/json");
        }};
        return doneRequest(mkcolUrl, "POST", HttpRequest.BodyPublishers.ofString(jsonObject.toString()), HttpResponse.BodyHandlers.ofString(), headers);
    }

    public static HttpResponse<String> parseTo(@NotBlank String source, @NotBlank String to, boolean async, Consumer<HttpResponse<String>> rollback) throws UnsupportedEncodingException {
        String config = getConfig(OWNCLOUD_CONFIG_CODE_ADMIN_NAME_AND_PASSWORD);
        String[] split = config.split(":");
        String name = split[0];
        // 定义请求URL
        String mkcolUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/dav/files/" + name + "/" + EncodeUtils.encodeChinese(source);
        HashMap<String, String> headers = new HashMap<>(1) {{
            put("Destination", getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/remote.php/dav/files/" + name + "/" + EncodeUtils.encodeChinese(to));
//            put("Destination", owncloudHomeUrl + "/remote.php/webdav/12345/aaab.pdf");
        }};
        return doneRequest(mkcolUrl, "COPY", HttpRequest.BodyPublishers.noBody(), HttpResponse.BodyHandlers.ofString(), headers, async, rollback);
    }

    private static <T> HttpResponse<T> doneRequest(String mkcolUrl, String method, HttpRequest.BodyPublisher body, HttpResponse.BodyHandler<T> responseBodyHandler, Map<String, String> headers) {
        return doneRequest(mkcolUrl, method, body, responseBodyHandler, headers, false, null);
    }

    private static <T> HttpResponse<T> doneRequest(String mkcolUrl, String method, HttpRequest.BodyPublisher body, HttpResponse.BodyHandler<T> responseBodyHandler, Map<String, String> headers, boolean async, @Nullable Consumer<HttpResponse<T>> rollback) {
        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
                .uri(URI.create(mkcolUrl))
                .method(method, body)
                .header("Authorization", "Basic " + Base64.encode(getConfig(OWNCLOUD_CONFIG_CODE_ADMIN_NAME_AND_PASSWORD)))
                .header("Accept", "*/*")
                .header("Accept-Encoding", "gzip, deflate, br")
                .header("Content-Type", "text/html; charset=UTF-8")
                .header("Cookie", AssertOverride.notBlank(getSessionCookie(false), "获取owncloud session失败"))
                .headers("userAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36");
        if (CollectionUtil.isNotEmpty(headers)) {
            //如有自定义，将原来的覆盖掉
            headers.forEach((hk, hv) -> {
                try {
                    requestBuilder.setHeader(hk, EncodeUtils.encodeChinese(hv));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        try {
            HttpClient client = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofMinutes(10))
                    .build();
            if (async) {
                CompletableFuture<HttpResponse<T>> reponse = client.sendAsync(requestBuilder.build(), responseBodyHandler);
                reponse.thenAccept(resp -> {
                            logger.info("doRequest, url: {}, method: {}, body: {}, resp:{}", mkcolUrl, method, body, resp);
                            if (Objects.nonNull(rollback)) {
                                rollback.accept(resp);
                            }
                        })
                        .orTimeout(5, TimeUnit.MINUTES)
                        .exceptionally(ex -> {
                            logger.error("doRequest, url: {}, method: {}, ex: {}", mkcolUrl, method, ex);
                            return null;
                        });
                return null;
            } else {
                HttpResponse<T> resp = client.send(requestBuilder.build(), responseBodyHandler);
                logger.info("doRequest, url: {}, method: {}, body: {}, resp:{}", mkcolUrl, method, body, resp);
                if (Objects.nonNull(rollback)) {
                    rollback.accept(resp);
                }
                return resp;
            }
        } catch (IOException e) {
            logger.error("异常", e);
            throw PlatformException.error("请求失败");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("异常", e);
            throw PlatformException.error("请求失败");
        }
    }

    public static String getSessionCookie(String username, String password, HttpServletResponse response) throws IOException {
        String sessionCookie = owncloudUtils.getSessionCookie(false, username + ":" + password);
        if (StringUtil.isNotEmpty(sessionCookie)) {
            //"oc39m2uq925x"
//            String sessionFilter = getConfig(OWNCLOUD_CONFIG_CODE_SESSION_FILTER, true);
            String homeUrl = getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL, true);
            String homeIp = StringUtils.isNotBlank(homeUrl) ? new URL(homeUrl).getHost() : null;
//            Set<String> set = Strings.isNotBlank(sessionFilter) ? Arrays.stream(sessionFilter.split(",")).collect(Collectors.toSet()) : Collections.EMPTY_SET;

//            response.setHeader("Access-Control-Allow-Credentials", "true");
//            response.setHeader("Access-Control-Allow-Headers", "*");
//            response.setHeader("Access-Control-Allow-Origin", "*");
//            response.setHeader("Access-Control-Allow-Methods", "PUT,GET,POST,DELETE, OPTIONS");
//            response.addHeader("Connection", "keep-alive");
            response.setHeader("Authorization", "Basic " + Base64.encode(username + ":" + password));

            Arrays.stream(sessionCookie.split(";"))
//                    .filter(s -> CollectionUtils.isEmpty(set) || set.stream().anyMatch(s::contains))
                    .forEach(s -> {
                        String[] arr = s.split("=");
                        Cookie cookie = new Cookie(arr[0], arr[1]);
                        cookie.setDomain(homeIp);
                        cookie.setMaxAge(60 * 60);
                        response.addCookie(cookie);
                    });
        } else {
            logger.error("获取owncloud-session失败");
        }
        return sessionCookie;
    }

    private static String getSessionCookie(boolean urlDecode) {
        return owncloudUtils.getSessionCookie(urlDecode, getConfig(OWNCLOUD_CONFIG_CODE_ADMIN_NAME_AND_PASSWORD));
    }

    /**
     * base method
     * cacheNames 设定要使用的cache的名字，必须提前定义好缓存
     * key 使用SpEL表达式来设定缓存的key，如果不设置默认方法上所有参数都会作为key的一部分
     * condition 使用SpEL表达式设定出发缓存的条件，在方法执行前生效
     * unless 使用SpEL设置不缓存的条件，这里是方法执行完生效，所以条件中可以有方法执行后的value   unless = "#result==#userId"
     * sync 用于同步的，在缓存失效（过期不存在等各种原因）的时候，如果多个线程同时访问被标注的方法则只允许一个线程通过去执行方法
     */
    @Cacheable(cacheNames = "owncloudSessionCookieCache", key = "#usernameAndPassword", condition = "#usernameAndPassword != null")
    public String getSessionCookie(boolean urlDecode, String usernameAndPassword) {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(getConfig(OWNCLOUD_CONFIG_CODE_HOME_URL) + "/index.php/apps/files/"))
                .method("GET", HttpRequest.BodyPublishers.noBody())
                .header("Accept", "*/*")
                .header("Accept-Encoding", "gzip, deflate, br")
                .header("Authorization", "Basic " + Base64.encode(usernameAndPassword))
                .build();
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (HttpStatus.OK.value() == response.statusCode()) {
                Map<String, List<String>> map = response.headers().map();
                System.out.println(map);
                Map<String, String> cookieMap = new HashMap<>();
                map.get("set-cookie").forEach(c -> {
                    String str1 = c.split(";")[0];
                    String[] sstr1 = str1.split("=");
                    cookieMap.put(sstr1[0], urlDecode ? URLDecoder.decode(sstr1[1], StandardCharsets.UTF_8) : sstr1[1]);
                });
                return cookieMap.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining(";"));
            } else {
                return Strings.EMPTY;
            }
        } catch (IOException e) {
            logger.error("异常", e);
            throw PlatformException.error("请求owncloud获取session失败");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("异常", e);
            throw PlatformException.error("请求owncloud获取session失败");
        }
    }
}
